import sys
import socket
import select
import logging
import errno


class EpollHandler(object):
    def __init__(self, chunk_size=512):
        self.chunk_size = chunk_size
        self.events = {}
        self.logger = logging.getLogger('EpollHandler')
        try:
            self.epollfd = select.epoll()
        except select.error, e:
            self.logger.debug('select.epoll() error: %s', e)
            sys.exit(-1)

    def set_base_event(self, sock, callback):
        self.epollfd.register(sock.fileno(), select.EPOLLIN)
        self.events[sock.fileno()] = (sock, callback, None, None)
        self.logger.debug('sock: %d', sock.fileno())

    def set_event(self, sock, rcallback, wcallback, rflag=0):
        try:
            self.epollfd.register(
                sock.fileno(), select.EPOLLOUT | select.EPOLLET)
        except select.error, e:
            sock.close()
            self.logger.debug('epollfd.register() error: %s', e)
            sys.exit(-1)
        if rflag:
            self.epollfd.modify(
                sock.fileno(), select.EPOLLIN | select.EPOLLET)
        self.events[sock.fileno()] = (sock, None, rcallback, wcallback)
        self.logger.debug('sock: %d', sock.fileno())

    def __read_data(self, fd):
        sock, rcallback = self.events[fd][0], self.events[fd][2]
        datas = ''
        while True:
            try:
                data = sock.recv(self.chunk_size)
                if not data and not datas:
                    self.epollfd.unregister(fd)
                    sock.close()
                    del(self.events[fd])
                    break
                else:
                    datas += data
            except socket.error, e:
                if e.errno == errno.EAGAIN:
                    if not rcallback:
                        self.logger.debug('Callback is None')
                        break
                    self.logger.debug('read datas (%s) from %d', datas, fd)
                    rcallback(datas)
                    self.epollfd.modify(fd, select.EPOLLET | select.EPOLLOUT)
                    break
                else:
                    self.logger.debug(
                        'unregister socke: %d', fd)
                    self.epollfd.unregister(fd)
                    sock.close()
                    del(self.events[fd])
                    break

    def __write_data(self, fd):
        sock, wcallback = self.events[fd][0], self.events[fd][3]
        if not wcallback:
            self.logger.debug('Callback is none')
            return
        data = wcallback()
        if not data:
            self.logger.debug('Data is none')
            return
        for i in range(0, len(data)):
            sendLen = 0
            while True:
                sendLen += sock.send(data[i][sendLen:])
                if sendLen == len(data[i]):
                    break
        self.logger.debug('write data (%s) to %d', ''.join(data), fd)
        data[:] = []

    def dispatcher(self):
        while True:
            epoll_list = self.epollfd.poll()
            for fd, events in epoll_list:
                if self.events.has_key(fd) and self.events[fd][1]:
                    sock, accept = self.events[fd][0], self.events[fd][1]
                    accept(sock)
                elif select.EPOLLIN & events:
                    self.__read_data(fd)
                elif select.EPOLLHUP & events:
                    sock = self.events[fd][0]
                    epollfd.unregister(fd)
                    sock.close()
                    del(self.events[fd])
                elif select.EPOLLOUT & events:
                    self.__write_data(fd)
                    self.epollfd.modify(fd, select.EPOLLIN | select.EPOLLET)
                else:
                    continue
